package com.sykj.why.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @author wy
 * @date 2018/11/5 14:16
 * @Desc 重写BeanUtils可选忽略空值
 */
public abstract class BeanUtils extends org.springframework.beans.BeanUtils {

    /**
     * @param source 源
     * @param target 目标
     * @param flag   是否忽略空值
     * @throws BeansException
     */
    public static void copyProperties(Object source, Object target, boolean flag)
            throws BeansException {

        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();
        // 获取target对象的PropertyDescriptor属性数组targetPds
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        // 遍历target对象属性
        for (PropertyDescriptor targetPd : targetPds) {
            // 获取其setter方法
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null) {
                // 获取source对象与target对象targetPd属性同名的PropertyDescriptor对象sourcePd
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    // 获取source对应属性的getter方法
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null &&
                            // 判断是否可赋值，如上述例子：desk是DeskVO、DeskDO，返回false；而chairs都是List<?>，返回true
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            // 通过内省获取source对象属性的值
                            Object value = readMethod.invoke(source);
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            // 通过反射给target对象属性赋值(判断是否忽略空值)
                            if (flag) {
                                if (!StringUtils.isEmpty(value)) {
                                    writeMethod.invoke(target, value);
                                }
                            } else {
                                writeMethod.invoke(target, value);
                            }
                        } catch (Throwable ex) {
                            throw new FatalBeanException(
                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
        try {
            String createTime = getProperty(source, "createTime");
            setProperty(target, "createTime", BaseTools.longToString(Long.parseLong(createTime)));
            String updateTime = getProperty(source, "updateTime");
            setProperty(target, "updateTime", BaseTools.longToString(Long.parseLong(updateTime)));
        } catch (IntrospectionException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取一个实体bean中某个字段的值
     *
     * @param bean  实体bean
     * @param field 字段名
     * @return
     */
    public static String getProperty(Object bean, String field) throws IntrospectionException, InvocationTargetException, IllegalAccessException {

        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field, bean.getClass());
        Method getMethod = propertyDescriptor.getReadMethod();
        Object value = getMethod.invoke(bean);
        return value.toString();
    }

    /**
     * 设置属性值
     *
     * @param bean
     * @param field
     * @param value
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static void setProperty(Object bean, String field, String value) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field, bean.getClass());
        Method setMethod = propertyDescriptor.getWriteMethod();
        setMethod.invoke(bean, value);
    }


}
